home *** CD-ROM | disk | FTP | other *** search
- Subject: v24i001: RCS source control system, Part01/12
- Newsgroups: comp.sources.unix
- Approved: rsalz@uunet.UU.NET
- X-Checksum-Snefru: 825fd1d0 b2dd0696 15275f29 d497c069
-
- Submitted-by: Adam Hammer <hammer@cs.purdue.edu>
- Posting-number: Volume 24, Issue 1
- Archive-name: rcs/part01
-
- RCS is a revision control system that keeps audit trails, edit histories,
- and so on. See the README file for more extensive details. GNU diff,
- useful for RCS, will follow this posting.
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: PACKNOTES README MANIFEST Makefile man src src/ci.c
- # Wrapped by rsalz@litchi.bbn.com on Thu Feb 21 14:36:53 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 1 (of 12)."'
- if test -f 'PACKNOTES' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'PACKNOTES'\"
- else
- echo shar: Extracting \"'PACKNOTES'\" \(78 characters\)
- sed "s/^X//" >'PACKNOTES' <<'END_OF_FILE'
- X# "rcs.ms" was split into 2 parts; to create it, do
- X cat rcs.ms.[1-9] >rcs.ms
- END_OF_FILE
- if test 78 -ne `wc -c <'PACKNOTES'`; then
- echo shar: \"'PACKNOTES'\" unpacked with wrong size!
- fi
- # end of 'PACKNOTES'
- fi
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(11703 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
- X Copyright 1990 by Paul Eggert
- X Distributed under license by the Free Software Foundation, Inc.
- X
- XThis file is part of RCS.
- X
- XRCS is free software; you can redistribute it and/or modify
- Xit under the terms of the GNU General Public License as published by
- Xthe Free Software Foundation; either version 1, or (at your option)
- Xany later version.
- X
- XRCS is distributed in the hope that it will be useful,
- Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
- XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- XGNU General Public License for more details.
- X
- XYou should have received a copy of the GNU General Public License
- Xalong with RCS; see the file COPYING. If not, write to
- Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- X
- XReport problems and direct all questions to:
- X
- X rcs-bugs@cs.purdue.edu
- X
- X*/
- X
- X$Id: README,v 5.6 1990/12/13 06:54:04 eggert Exp $
- X
- XThis directory contains complete sources for RCS version 5.5.
- X
- X
- XInstallation notes:
- X
- X RCS requires a diff that supports the -n option.
- X Get GNU diff (version 1.15 or later) if your diff lacks -n.
- X
- X RCS works best with a diff that supports -a, -L, and (diff3 only) -m.
- X GNU diff supports these options.
- X
- X Sources for RCS are in the src directory.
- X Read the directions in src/Makefile to set up the options
- X for building RCS on your system.
- X If `make' fails to build src/conf.h, look in src/conf.error
- X to see what went wrong in the src/conf.sh shell file.
- X If all else fails, create src/conf.h manually by editing a
- X copy of src/conf.heg.
- X
- X Manual entries reside in man.
- X
- X To test your installation of RCS, run the shell file src/rcstest.
- X
- X Troff source for the paper `RCS--A System for Version Control', which
- X appeared in _Software--Practice & Experience_, is in rcs.ms.
- X
- X
- XRCS compatibility notes:
- X
- X RCS version 5 reads RCS files written by any RCS version released since 1982.
- X It also writes RCS files that these older versions of RCS can read,
- X unless you use one of the following new features:
- X
- X checkin times after 1999/12/31 23:59:59 GMT
- X checking in non-text files
- X non-Ascii symbolic names
- X rcs -bX, where X is nonempty
- X rcs -kX, where X is not `kv'
- X RCS files that exceed hardcoded limits in older RCS versions
- X
- X
- XFeatures new to RCS version 5 include:
- X
- X RCS can check in arbitrary files, not just text files, if diff -a works.
- X RCS can merge lines containing just a single `.' if diff3 -m works.
- X GNU diff supports the -a and -m options.
- X
- X RCS can now be installed as a setgid or setuid program
- X if the setegid() and seteuid() system calls work.
- X Setid privileges yield extra security if RCS files are protected so that
- X only the effective group or user can write RCS directories.
- X RCS uses the real group and user for all accesses other than to RCS files.
- X On older hosts lacking setegid() and seteuid(), RCS uses the effective group
- X and user for all accesses; formerly it was inconsistent.
- X
- X New options to co, rcsdiff, and rcsmerge give more flexibility to keyword
- X substitution.
- X
- X -kkv substitutes the default `$Keyword: value $' for keyword strings.
- X However, a locker's name is inserted only as a file is being locked,
- X i.e. by `ci -l' and `co -l'. This is normally the default.
- X
- X -kkvl acts like -kkv, except that a locker's name is always inserted
- X if the given revision is currently locked. This was the default in
- X version 4. It is now the default only with when using rcsdiff to
- X compare a revision to a working file whose mode is that of a file
- X checked out for changes.
- X
- X -kk substitutes just `$Keyword$', which helps to ignore keyword values
- X when comparing revisions.
- X
- X -ko retrieves the old revision's keyword string, thus bypassing keyword
- X substitution.
- X
- X -kv retrieves just `value'. This can ease the use of keyword values, but
- X it is dangerous because it causes RCS to lose track of where the keywords
- X are, so for safety the owner write permission of the working file is
- X turned off when -kv is used; to edit the file later, check it out again
- X without -kv.
- X
- X rcs -ko sets the default keyword substitution to be in the style of co -ko,
- X and similarly for the other -k options. This can be useful with binary file
- X formats that cannot tolerate changing the lengths of keyword strings.
- X However it also renders a RCS file readable only by RCS version 5 or later.
- X Use rcs -kkv to restore the usual default substitution.
- X
- X RCS can now be used by development groups that span timezone boundaries.
- X All times are now displayed in GMT, and GMT is the default timezone.
- X To use local time with co -d, append ` LT' to the time.
- X When interchanging RCS files with sites running older versions of RCS,
- X users may encounter discrepancies of up to 13 hours in old time stamps.
- X The list of timezone names has been modernized.
- X
- X Dates are now displayed using four-digit years, not two-digit years.
- X Years given in -d options must now have four digits.
- X This change is required for RCS to continue to work after 1999/12/31.
- X The form of dates in version 5 RCS files will not change until 2000/01/01,
- X so in the meantime RCS files can still be interchanged with sites
- X running older versions of RCS. To make room for the longer dates,
- X rlog now outputs `lines: +A -D' instead of `lines added/del: A/D'.
- X
- X To help prevent diff programs that are broken or have run out of memory
- X from trashing an RCS file, ci now checks diff output more carefully.
- X
- X ci -k now handles the Log keyword, so that checking in a file
- X with -k does not normally alter the file's contents.
- X
- X RCS no longer outputs white space at the ends of lines
- X unless the original working file had it.
- X For consistency with other keywords,
- X a space, not a tab, is now output after `$Log:'.
- X Rlog now puts lockers and symbolic names on separate lines in the output
- X to avoid generating lines that are too long.
- X A similar fix has been made to lists in the RCS files themselves.
- X
- X RCS no longer outputs the string `Locker: ' when expanding Header or Id
- X keywords. This saves space and reverts back to version 3 behavior.
- X
- X The default branch is not put into the RCS file unless it is nonempty.
- X Therefore, files generated by RCS version 5 can be read by RCS version 3
- X unless they use the default branch feature introduced in version 4.
- X This fixes a compatibility problem introduced by version 4.
- X
- X RCS can now emulate older versions of RCS; see `co -V'.
- X This may be useful to overcome compatibility problems
- X due to the above changes.
- X
- X Programs like Emacs can now interact with RCS commands via a pipe:
- X the new -I option causes ci, co, and rcs to run interactively,
- X even if standard input is not a terminal.
- X These commands now accept multiple inputs from stdin separated by `.' lines.
- X
- X ci now silently ignores the -t option if the RCS file already exists.
- X This simplifies some shell scripts and improves security in setuid sites.
- X
- X Descriptive text may be given directly in an argument of the form -t-string.
- X
- X The character set for symbolic names has been upgraded
- X from Ascii to ISO 8859.
- X
- X rcsdiff now passes through all options used by GNU diff;
- X this is a longer list than 4.3BSD diff.
- X
- X merge's new -L option gives tags for merge's overlap report lines.
- X This ability used to be present in a different, undocumented form;
- X the new form is chosen for compatibility with GNU diff3's -L option.
- X
- X rcsmerge and merge now have a -q option, just like their siblings do.
- X
- X RCS now attempts to ignore parts of an RCS file that look like they come
- X from a future version of RCS.
- X
- X When properly configured, RCS now strictly conforms with Posix 1003.1-1988.
- X Normally, RCS file names contain `,', which is outside the Posix portable
- X filename character set; but in impoverished Posix environments, you can
- X compile RCS so that the RCS file for Foo is named just RCS/Foo.
- X RCS can still be compiled in non-Posix traditional Unix environments,
- X and can use common BSD and USG extensions to Posix.
- X RCS is a conforming ANSI C program, and also compiles under traditional C.
- X
- X Arbitrary limits on internal table sizes have been removed.
- X The only limit now is the amount of memory available via malloc().
- X
- X File temporaries, lock files, signals, and system call return codes
- X are now handled more cleanly, portably, and quickly.
- X Some race conditions have been removed.
- X
- X A new compile-time option RCSPREFIX lets administrators avoid absolute path
- X names for subsidiary programs, trading speed for flexibility.
- X
- X The configuration procedure is now more automatic.
- X
- X Snooping has been removed; it did not work in version 4.
- X
- X
- XVersion 4 was the first version distributed by FSF.
- XBeside bug fixes, features new to RCS version 4 include:
- X
- X The notion of default branch has been added; see rcs -b.
- X
- X
- XVersion 3 was included in the 4.3BSD distribution.
- X
- X
- XFurther projects:
- X
- X Improve performance when checking out branch revisions;
- X see the `piece table' comments in rcs.ms.
- X Joe Berkovitz of Stratus has written some fast revision extraction code;
- X unfortunately there wasn't enough time to integrate it into RCS version 5.
- X It's probably best to use mmap() here if available.
- X
- X Let the user mark an RCS revision as deleted; checking out such a revision
- X would result in no working file. Similarly, using `co -d' with a date either
- X before the initial revision or after the file was marked deleted should
- X remove the working file. For extra credit, extend the notion of `deleted' to
- X include `renamed', i.e. when an RCS file gets renamed.
- X
- X Use a better scheme for locking revisions; the current scheme requires
- X changing the RCS file just to lock or unlock a revision.
- X The new scheme should coexist as well as possible with older versions of RCS.
- X
- X Permit multiple option-filename pairs, e.g. co -r1.4 a -r1.5 b.
- X
- X Add rcs options for changing keyword names, e.g. XConsortium instead of Id.
- X
- X If there are multiple locks by a user, ci should fall back on ci -k's
- X method to figure out which version it is.
- X
- X Add frozen branches a la SCCS. In general, be able to emulate all of
- X SCCS, so that an SCCS-to-RCS program can be practical.
- X
- X Improve RCS's method for storing binary files.
- X Although it is more efficient than SCCS's,
- X the diff algorithm is still line oriented,
- X and often generates long output for minor changes to an executable file.
- X
- X Port binary file handling to non-Unix hosts where fopen(F,"r") and
- X fopen(F,"rb") are quite different beasts.
- X
- X Extend the grammar of RCS files so that keywords need not be in a fixed order.
- X
- X Clean up the source code with a consistent indenting style.
- X
- X Update the date parser to use the more modern getdate.y by Bellovin, Salz,
- X and Berets.
- X
- X Internationalize messages; unfortunately, there's no common standard yet.
- X
- X Prune the unnecessary keyword substitution baggage from the rcs command.
- X
- X Break up the code into a library so that it's easier to write new programs
- X that manipulate RCS files.
- X
- X
- XCredits:
- X
- X RCS was designed and built by Walter F. Tichy of Purdue University.
- X RCS version 3 was released in 1983.
- X
- X Thomas Narten, Dan Trinkle, and others of Purdue supported RCS through
- X version 4.2, released in 1989. Guy Harris of Sun contributed many porting
- X fixes. Paul Eggert of System Development Corporation contributed bug fixes
- X and tuneups. Jay Lepreau contributed 4.3BSD support.
- X
- X Paul Eggert of Twin Sun wrote the changes for RCS version 5, released in
- X 1990. Ideas for setgid support were contributed by Bill Hahn of Stratus.
- X Test case ideas were contributed by Matt Cross of Stratus.
- X Adam Hammer of Purdue QAed.
- END_OF_FILE
- if test 11703 -ne `wc -c <'README'`; then
- echo shar: \"'README'\" unpacked with wrong size!
- fi
- # end of 'README'
- fi
- if test -f 'MANIFEST' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'MANIFEST'\"
- else
- echo shar: Extracting \"'MANIFEST'\" \(1641 characters\)
- sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
- X File Name Archive # Description
- X----------------------------------------------------------
- XPACKNOTES 1 Warnings about long lines, etc
- XREADME 1
- XMANIFEST 1
- XCOPYING 8
- XMakefile 1
- Xman 1
- Xman/Makefile 12
- Xman/ci.1 9
- Xman/co.1 9
- Xman/ident.1 12
- Xman/merge.1 11
- Xman/rcs.1 11
- Xman/rcsclean.1 11
- Xman/rcsdiff.1 11
- Xman/rcsfile.5 11
- Xman/rcsfreeze.1 12
- Xman/rcsintro.1 10
- Xman/rcsmerge.1 11
- Xman/rlog.1 6
- Xrcs.ms.01 2 (part 1)
- Xrcs.ms.02 11 (part 2)
- Xrcs_func.ms 10
- Xsrc 1
- Xsrc/Makefile 3
- Xsrc/ci.c 1
- Xsrc/co.c 7
- Xsrc/conf.heg 11
- Xsrc/conf.sh 9
- Xsrc/ident.c 11
- Xsrc/maketime.c 10
- Xsrc/merge.sh 11
- Xsrc/partime.c 8
- Xsrc/rcs.c 3
- Xsrc/rcsbase.h 6
- Xsrc/rcsclean.sh 12
- Xsrc/rcsdiff.c 10
- Xsrc/rcsedit.c 6
- Xsrc/rcsfcmp.c 7
- Xsrc/rcsfnms.c 5
- Xsrc/rcsfreeze.sh 11
- Xsrc/rcsgen.c 9
- Xsrc/rcskeep.c 10
- Xsrc/rcskeys.c 2
- Xsrc/rcslex.c 5
- Xsrc/rcsmap.c 11
- Xsrc/rcsmerge.c 10
- Xsrc/rcsrev.c 7
- Xsrc/rcssyn.c 8
- Xsrc/rcsutil.c 4
- Xsrc/rlog.c 4
- END_OF_FILE
- if test 1641 -ne `wc -c <'MANIFEST'`; then
- echo shar: \"'MANIFEST'\" unpacked with wrong size!
- fi
- # end of 'MANIFEST'
- fi
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(300 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- XSUBDIR= src man
- XDESTDIR=
- X
- Xall: ${SUBDIR}
- X
- X${SUBDIR}: FRC
- X cd $@; make ${MFLAGS} DESTDIR=${DESTDIR}
- X
- Xinstall:
- X for i in ${SUBDIR}; do \
- X (cd $$i; make ${MFLAGS} DESTDIR=${DESTDIR} install); \
- X done
- X
- Xclean:
- X for i in ${SUBDIR}; do \
- X (cd $$i; make ${MFLAGS} DESTDIR=${DESTDIR} clean); \
- X done
- X
- XFRC:
- X
- END_OF_FILE
- if test 300 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- fi
- if test ! -d 'man' ; then
- echo shar: Creating directory \"'man'\"
- mkdir 'man'
- fi
- if test ! -d 'src' ; then
- echo shar: Creating directory \"'src'\"
- mkdir 'src'
- fi
- if test -f 'src/ci.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/ci.c'\"
- else
- echo shar: Extracting \"'src/ci.c'\" \(34353 characters\)
- sed "s/^X//" >'src/ci.c' <<'END_OF_FILE'
- X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
- X Copyright 1990 by Paul Eggert
- X Distributed under license by the Free Software Foundation, Inc.
- X
- XThis file is part of RCS.
- X
- XRCS is free software; you can redistribute it and/or modify
- Xit under the terms of the GNU General Public License as published by
- Xthe Free Software Foundation; either version 1, or (at your option)
- Xany later version.
- X
- XRCS is distributed in the hope that it will be useful,
- Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
- XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- XGNU General Public License for more details.
- X
- XYou should have received a copy of the GNU General Public License
- Xalong with RCS; see the file COPYING. If not, write to
- Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- X
- XReport problems and direct all questions to:
- X
- X rcs-bugs@cs.purdue.edu
- X
- X*/
- X
- X/*
- X * RCS checkin operation
- X */
- X/*******************************************************************
- X * check revisions into RCS files
- X *******************************************************************
- X */
- X
- X
- X
- X/* $Log: ci.c,v $
- X * Revision 5.12 1990/12/31 01:00:12 eggert
- X * Don't use uninitialized storage when handling -{N,n}.
- X *
- X * Revision 5.11 1990/12/04 05:18:36 eggert
- X * Use -I for prompts and -q for diagnostics.
- X *
- X * Revision 5.10 1990/11/05 20:30:10 eggert
- X * Don't remove working file when aborting due to no changes.
- X *
- X * Revision 5.9 1990/11/01 05:03:23 eggert
- X * Add -I and new -t behavior. Permit arbitrary data in logs.
- X *
- X * Revision 5.8 1990/10/04 06:30:09 eggert
- X * Accumulate exit status across files.
- X *
- X * Revision 5.7 1990/09/25 20:11:46 hammer
- X * fixed another small typo
- X *
- X * Revision 5.6 1990/09/24 21:48:50 hammer
- X * added cleanups from Paul Eggert.
- X *
- X * Revision 5.5 1990/09/21 06:16:38 hammer
- X * made it handle multiple -{N,n}'s. Also, made it treat re-directed stdin
- X * the same as the terminal
- X *
- X * Revision 5.4 1990/09/20 02:38:51 eggert
- X * ci -k now checks dates more thoroughly.
- X *
- X * Revision 5.3 1990/09/11 02:41:07 eggert
- X * Fix revision bug with `ci -k file1 file2'.
- X *
- X * Revision 5.2 1990/09/04 08:02:10 eggert
- X * Permit adjacent revisions with identical time stamps (possible on fast hosts).
- X * Improve incomplete line handling. Standardize yes-or-no procedure.
- X *
- X * Revision 5.1 1990/08/29 07:13:44 eggert
- X * Expand locker value like co. Clean old log messages too.
- X *
- X * Revision 5.0 1990/08/22 08:10:00 eggert
- X * Don't require a final newline.
- X * Make lock and temp files faster and safer.
- X * Remove compile-time limits; use malloc instead.
- X * Permit dates past 1999/12/31. Switch to GMT.
- X * Add setuid support. Don't pass +args to diff. Check diff's output.
- X * Ansify and Posixate. Add -k, -V. Remove snooping. Tune.
- X * Check diff's output.
- X *
- X * Revision 4.9 89/05/01 15:10:54 narten
- X * changed copyright header to reflect current distribution rules
- X *
- X * Revision 4.8 88/11/08 13:38:23 narten
- X * changes from root@seismo.CSS.GOV (Super User)
- X * -d with no arguments uses the mod time of the file it is checking in
- X *
- X * Revision 4.7 88/08/09 19:12:07 eggert
- X * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one.
- X * Use execv(), not system(); allow cc -R; remove lint.
- X * isatty(fileno(stdin)) -> ttystdin()
- X *
- X * Revision 4.6 87/12/18 11:34:41 narten
- X * lint cleanups (from Guy Harris)
- X *
- X * Revision 4.5 87/10/18 10:18:48 narten
- X * Updating version numbers. Changes relative to revision 1.1 are actually
- X * relative to 4.3
- X *
- X * Revision 1.3 87/09/24 13:57:19 narten
- X * Sources now pass through lint (if you ignore printf/sprintf/fprintf
- X * warnings)
- X *
- X * Revision 1.2 87/03/27 14:21:33 jenkins
- X * Port to suns
- X *
- X * Revision 4.3 83/12/15 12:28:54 wft
- X * ci -u and ci -l now set mode of working file properly.
- X *
- X * Revision 4.2 83/12/05 13:40:54 wft
- X * Merged with 3.9.1.1: added calls to clearerr(stdin).
- X * made rewriteflag external.
- X *
- X * Revision 4.1 83/05/10 17:03:06 wft
- X * Added option -d and -w, and updated assingment of date, etc. to new delta.
- X * Added handling of default branches.
- X * Option -k generates std. log message; fixed undef. pointer in reading of log.
- X * Replaced getlock() with findlock(), link--unlink with rename(),
- X * getpwuid() with getcaller().
- X * Moved all revision number generation to new routine addelta().
- X * Removed calls to stat(); now done by pairfilenames().
- X * Changed most calls to catchints() with restoreints().
- X * Directed all interactive messages to stderr.
- X *
- X * Revision 3.9.1.1 83/10/19 04:21:03 lepreau
- X * Added clearerr(stdin) to getlogmsg() for re-reading stdin.
- X *
- X * Revision 3.9 83/02/15 15:25:44 wft
- X * 4.2 prerelease
- X *
- X * Revision 3.9 83/02/15 15:25:44 wft
- X * Added call to fastcopy() to copy remainder of RCS file.
- X *
- X * Revision 3.8 83/01/14 15:34:05 wft
- X * Added ignoring of interrupts while new RCS file is renamed;
- X * Avoids deletion of RCS files by interrupts.
- X *
- X * Revision 3.7 82/12/10 16:09:20 wft
- X * Corrected checking of return code from diff.
- X *
- X * Revision 3.6 82/12/08 21:34:49 wft
- X * Using DATEFORM to prepare date of checked-in revision;
- X * Fixed return from addbranch().
- X *
- X * Revision 3.5 82/12/04 18:32:42 wft
- X * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated
- X * field lockedby in removelock(), moved getlogmsg() before calling diff.
- X *
- X * Revision 3.4 82/12/02 13:27:13 wft
- X * added option -k.
- X *
- X * Revision 3.3 82/11/28 20:53:31 wft
- X * Added mustcheckin() to check for redundant checkins.
- X * Added xpandfile() to do keyword expansion for -u and -l;
- X * -m appends linefeed to log message if necessary.
- X * getlogmsg() suppresses prompt if stdin is not a terminal.
- X * Replaced keeplock with lockflag, fclose() with ffclose(),
- X * %02d with %.2d, getlogin() with getpwuid().
- X *
- X * Revision 3.2 82/10/18 20:57:23 wft
- X * An RCS file inherits its mode during the first ci from the working file,
- X * otherwise it stays the same, except that write permission is removed.
- X * Fixed ci -l, added ci -u (both do an implicit co after the ci).
- X * Fixed call to getlogin(), added call to getfullRCSname(), added check
- X * for write error.
- X * Changed conflicting identifiers.
- X *
- X * Revision 3.1 82/10/13 16:04:59 wft
- X * fixed type of variables receiving from getc() (char -> int).
- X * added include file dbm.h for getting BYTESIZ. This is used
- X * to check the return code from diff portably.
- X */
- X
- X#include "rcsbase.h"
- X
- Xstruct Symrev {
- X const char *ssymbol;
- X int override;
- X struct Symrev * nextsym;
- X};
- X
- X/* rcsfcmp */
- Xint rcsfcmp P((const char*,const char*,const struct hshentry*));
- X
- X/* rcskeep */
- Xextern char prevdate[];
- Xextern struct buf prevauthor, prevrev, prevstate;
- Xint getoldkeys P((FILE*));
- X
- Xstatic const char *xpandfile P((const char*,const char*,const struct hshentry*));
- Xstatic const char *getdate P((void));
- Xstatic int addbranch P((struct hshentry*,struct buf*));
- Xstatic int addelta P((void));
- Xstatic int mustcheckin P((const char*,const struct hshentry*));
- Xstatic struct cbuf getlogmsg P((void));
- Xstatic struct hshentry *removelock P((struct hshentry*));
- Xstatic void cleanup P((void));
- Xstatic void incnum P((const char*,struct buf*));
- Xstatic void addassoclst P((int, char *));
- X
- Xstatic const char diff[] = DIFF;
- X
- Xstatic FILE *workptr; /* working file pointer */
- Xstatic const char *olddeltanum; /* number of old delta */
- Xstatic struct buf newdelnum; /* new revision number */
- Xstatic struct cbuf msg;
- Xstatic int exitstatus;
- Xstatic int forceciflag; /* forces check in */
- Xstatic int keepflag, keepworkingfile, rcsinitflag;
- Xstatic struct hshentries *gendeltas; /* deltas to be generated */
- Xstatic struct hshentry *targetdelta; /* old delta to be generated */
- Xstatic struct hshentry newdelta; /* new delta to be inserted */
- Xstatic struct Symrev *assoclst, *lastassoc;
- X
- XmainProg(ciId, "ci", "$Id: ci.c,v 5.12 1990/12/31 01:00:12 eggert Exp $")
- X{
- X static const char cmdusage[] =
- X "\nci usage: ci -{fklqru}[rev] -mmsg -{nN}name -sstate -t[textfile] -Vn file ...";
- X
- X char altdate[datesize];
- X const char *author, *krev, *rev, *state, *textfile;
- X const char *diffilename, *expfilename;
- X const char *workdiffname, *newworkfilename;
- X int exit_stats; /* return code for command invocations */
- X int lockflag;
- X int r;
- X int usestatdate; /* Use mod time of file for -d. */
- X mode_t newRCSmode; /* mode for RCS file */
- X mode_t newworkmode; /* mode for working file */
- X struct Symrev *curassoc;
- X
- X initid();
- X catchints();
- X
- X author = rev = state = textfile = nil;
- X curassoc = assoclst = lastassoc = (struct Symrev *) nil;
- X lockflag = false;
- X altdate[0]= '\0'; /* empty alternate date for -d */
- X usestatdate=false;
- X
- X while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
- X switch ((*argv)[1]) {
- X
- X case 'r':
- X keepworkingfile = lockflag = false;
- X revno: if ((*argv)[2]!='\0') {
- X if (rev) warn("redefinition of revision number");
- X rev = (*argv)+2;
- X }
- X break;
- X
- X case 'l':
- X keepworkingfile=lockflag=true;
- X goto revno;
- X
- X case 'u':
- X keepworkingfile=true; lockflag=false;
- X goto revno;
- X
- X case 'I':
- X interactiveflag = true;
- X goto revno;
- X
- X case 'q':
- X quietflag=true;
- X goto revno;
- X
- X case 'f':
- X forceciflag=true;
- X goto revno;
- X
- X case 'k':
- X keepflag=true;
- X goto revno;
- X
- X case 'm':
- X if (msg.size) redefined('m');
- X msg = cleanlogmsg(*argv+2, strlen(*argv+2));
- X if (!msg.size)
- X warn("missing message for -m option");
- X break;
- X
- X case 'n':
- X if ((*argv)[2] == '\0') {
- X error("missing symbolic name after -n");
- X break;
- X }
- X checksid((*argv)+2);
- X addassoclst(false, (*argv)+2);
- X break;
- X
- X case 'N':
- X if ((*argv)[2] == '\0') {
- X error("missing symbolic name after -N");
- X break;
- X }
- X checksid((*argv)+2);
- X addassoclst(true, (*argv)+2);
- X break;
- X
- X case 's':
- X if ((*argv)[2]!='\0'){
- X if (state) redefined('s');
- X checksid((*argv)+2);
- X state = (*argv)+2;
- X } else
- X warn("missing state for -s option");
- X break;
- X
- X case 't':
- X if ((*argv)[2]!='\0'){
- X if (textfile) redefined('t');
- X textfile = (*argv)+2;
- X }
- X break;
- X
- X case 'd':
- X if (altdate[0] || usestatdate)
- X redefined('d');
- X altdate[0] = 0;
- X usestatdate = false;
- X if ((*argv)[2])
- X str2date(*argv+2, altdate);
- X else
- X usestatdate = true;
- X break;
- X
- X case 'w':
- X if ((*argv)[2]!='\0'){
- X if (author) redefined('w');
- X checksid((*argv)+2);
- X author = (*argv)+2;
- X } else
- X warn("missing author for -w option");
- X break;
- X
- X case 'V':
- X setRCSversion(*argv);
- X break;
- X
- X
- X
- X default:
- X faterror("unknown option: %s%s", *argv, cmdusage);
- X };
- X } /* end processing of options */
- X
- X if (argc<1) faterror("no input file%s", cmdusage);
- X
- X /* now handle all filenames */
- X do {
- X finptr=frewrite=NULL;
- X fcopy = foutptr = NULL;
- X workptr = NULL;
- X targetdelta=nil;
- X olddeltanum=nil;
- X ffree();
- X
- X switch (pairfilenames(argc, argv, rcswriteopen, false, false)) {
- X
- X case -1: /* New RCS file */
- X rcsinitflag = true;
- X break;
- X
- X case 0: /* Error */
- X continue;
- X
- X case 1: /* Normal checkin with prev . RCS file */
- X rcsinitflag = !Head;
- X }
- X
- X /* now RCSfilename contains the name of the RCS file, and
- X * workfilename contains the name of the working file.
- X * If the RCS file exists, finptr contains the file descriptor for the
- X * RCS file. The admin node is initialized.
- X * RCSstat is set.
- X */
- X
- X diagnose("%s <-- %s\n", RCSfilename,workfilename);
- X
- X errno = 0;
- X if (!(workptr = fopen(workfilename,"r"))) {
- X eerror(workfilename);
- X continue;
- X }
- X if (!getfworkstat(fileno(workptr))) continue;
- X newRCSmode =
- X (rcsinitflag ? workstat.st_mode : RCSstat.st_mode)
- X & ~(S_IWUSR|S_IWGRP|S_IWOTH);
- X /* newRCSmode also adjusts mode of working file for -u and -l. */
- X if (finptr && !checkaccesslist()) continue; /* give up */
- X
- X krev = rev;
- X if (keepflag) {
- X /* get keyword values from working file */
- X if (!getoldkeys(workptr)) continue;
- X if (!rev && !*(krev = prevrev.string)) {
- X error("can't find a revision number in %s",workfilename);
- X continue;
- X }
- X if (*prevdate=='\0' && *altdate=='\0' && usestatdate==false)
- X warn("can't find a date in %s", workfilename);
- X if (!*prevauthor.string && !author)
- X warn("can't find an author in %s", workfilename);
- X if (!*prevstate.string && !state)
- X warn("can't find a state in %s", workfilename);
- X } /* end processing keepflag */
- X
- X gettree(); /* reads in the delta tree.*/
- X
- X /* expand symbolic revision number */
- X if (!expandsym(krev,&newdelnum)) continue;
- X
- X /* splice new delta into tree */
- X if (!addelta()) continue;
- X
- X if (rcsinitflag) {
- X diagnose("initial revision: %s\n", newdelnum.string);
- X } else diagnose("new revision: %s; previous revision: %s\n",
- X newdelnum.string, olddeltanum);
- X
- X newdelta.num = newdelnum.string;
- X newdelta.branches=nil;
- X newdelta.lockedby=nil; /*might be changed by addlock() */
- X newdelta.selector = true;
- X /* set author */
- X if (author!=nil)
- X newdelta.author=author; /* set author given by -w */
- X else if (keepflag && *prevauthor.string)
- X newdelta.author=prevauthor.string; /* preserve old author if possible*/
- X else newdelta.author=getcaller();/* otherwise use caller's id */
- X if (state!=nil)
- X newdelta.state=state; /* set state given by -s */
- X else if (keepflag && *prevstate.string)
- X newdelta.state=prevstate.string; /* preserve old state if possible */
- X else newdelta.state=DEFAULTSTATE;/* otherwise use default state */
- X if (usestatdate) {
- X time2date(workstat.st_mtime, altdate);
- X }
- X if (*altdate!='\0')
- X newdelta.date=altdate; /* set date given by -d */
- X else if (keepflag && *prevdate) /* preserve old date if possible */
- X newdelta.date = prevdate;
- X else
- X newdelta.date = getdate(); /* use current date */
- X /* now check validity of date -- needed because of -d and -k */
- X if (targetdelta!=nil &&
- X cmpnum(newdelta.date,targetdelta->date) < 0) {
- X error("Date %s precedes %s in existing revision %s.",
- X newdelta.date,targetdelta->date, targetdelta->num);
- X continue;
- X }
- X
- X
- X if (lockflag && addlock(&newdelta) < 0) continue;
- X curassoc = assoclst;
- X while (curassoc) {
- X if (!addsymbol(newdelta.num, curassoc->ssymbol, curassoc->override))
- X break;
- X curassoc = curassoc->nextsym;
- X }
- X if (curassoc) continue;
- X
- X
- X putadmin(frewrite);
- X puttree(Head,frewrite);
- X putdesc(false,textfile);
- X
- X
- X /* build rest of file */
- X if (rcsinitflag) {
- X /* get logmessage */
- X newdelta.log=getlogmsg();
- X if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue;
- X } else {
- X diffilename = maketemp(0);
- X workdiffname = workfilename;
- X if (workdiffname[0] == '+') {
- X /* Some diffs have options with leading '+'. */
- X char *w = ftnalloc(char, strlen(workfilename)+3);
- X workdiffname = w;
- X *w++ = '.';
- X *w++ = SLASH;
- X VOID strcpy(w, workfilename);
- X }
- X if (&newdelta==Head) {
- X /* prepend new one */
- X foutptr = NULL;
- X if (!(expfilename=
- X buildrevision(gendeltas,targetdelta,false,false))) continue;
- X if (!mustcheckin(expfilename,targetdelta)) continue;
- X /* don't check in files that aren't different, unless forced*/
- X newdelta.log=getlogmsg();
- X exit_stats = run((char*)nil,diffilename,
- X diff DIFF_FLAGS, workdiffname, expfilename,
- X (char*)nil);
- X if (!WIFEXITED(exit_stats) || 1<WEXITSTATUS(exit_stats))
- X faterror ("diff failed");
- X /* diff status is EXIT_TROUBLE on failure. */
- X if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue;
- X if (!putdtext(olddeltanum,targetdelta->log,diffilename,frewrite,true)) continue;
- X } else {
- X /* insert new delta text */
- X foutptr = frewrite;
- X if (!(expfilename=
- X buildrevision(gendeltas,targetdelta,false,false))) continue;
- X if (!mustcheckin(expfilename,targetdelta)) continue;
- X /* don't check in files that aren't different, unless forced*/
- X newdelta.log=getlogmsg();
- X exit_stats = run((char*)nil, diffilename,
- X diff DIFF_FLAGS, expfilename, workdiffname,
- X (char*)nil);
- X if (!WIFEXITED(exit_stats) || 1<WEXITSTATUS(exit_stats))
- X faterror ("diff failed");
- X if (!putdtext(newdelnum.string,newdelta.log,diffilename,frewrite,true)) continue;
- X }
- X
- X /* rewrite rest of RCS file */
- X fastcopy(finptr,frewrite);
- X ffclose(finptr); finptr=NULL; /* Help the file system. */
- X }
- X ffclose(frewrite); frewrite=NULL;
- X ffclose(workptr); workptr=NULL;
- X seteid();
- X if ((r = chmod(newRCSfilename,newRCSmode)) == 0) {
- X ignoreints();
- X r = re_name(newRCSfilename,RCSfilename);
- X keepdirtemp(newRCSfilename);
- X restoreints();
- X }
- X setrid();
- X if (r != 0) {
- X eerror(RCSfilename);
- X error("saved in %s", newRCSfilename);
- X dirtempunlink();
- X break;
- X }
- X
- X if (!keepworkingfile) {
- X r = unlink(workfilename); /* Get rid of old file */
- X } else {
- X newworkmode = WORKMODE(newRCSmode,
- X !(Expand == OLD_EXPAND || !lockflag && StrictLocks)
- X );
- X /* Expand if !OLD_EXPAND, or if mode can't be fixed. */
- X if (
- X Expand != OLD_EXPAND
- X || ( workstat.st_mode != newworkmode
- X && (r = chmod(workfilename,newworkmode)) < 0
- X )
- X ) {
- X /* Expand keywords in file. */
- X locker_expansion = lockflag;
- X newworkfilename=
- X xpandfile(workfilename,workfilename /*for directory*/,&newdelta);
- X if (!newworkfilename) continue; /* expand failed */
- X if ((r = chmod(newworkfilename, newworkmode)) == 0) {
- X ignoreints();
- X r = re_name(newworkfilename,workfilename);
- X keepdirtemp(newworkfilename);
- X restoreints();
- X }
- X }
- X }
- X if (r != 0) {
- X eerror(workfilename);
- X continue;
- X }
- X diagnose("done\n");
- X
- X } while (cleanup(),
- X ++argv, --argc >=1);
- X
- X tempunlink();
- X exitmain(exitstatus);
- X} /* end of main (ci) */
- X
- X static void
- Xcleanup()
- X{
- X if (nerror) exitstatus = EXIT_FAILURE;
- X if (finptr) ffclose(finptr);
- X if (frewrite) ffclose(frewrite);
- X if (workptr) ffclose(workptr);
- X dirtempunlink();
- X}
- X
- X#if lint
- X# define exiterr ciExit
- X#endif
- X exiting void
- Xexiterr()
- X{
- X dirtempunlink();
- X tempunlink();
- X _exit(EXIT_FAILURE);
- X}
- X
- X/*****************************************************************/
- X/* the rest are auxiliary routines */
- X
- X
- X static int
- Xaddelta()
- X/* Function: Appends a delta to the delta tree, whose number is
- X * given by newdelnum. Updates Head, newdelnum, newdelnumlength,
- X * olddeltanum and the links in newdelta.
- X * Returns false on error, true on success.
- X */
- X{
- X register char *tp;
- X register unsigned i;
- X unsigned newdnumlength; /* actual length of new rev. num. */
- X
- X newdnumlength = countnumflds(newdelnum.string);
- X
- X if (rcsinitflag) {
- X /* this covers non-existing RCS file and a file initialized with rcs -i */
- X if ((newdnumlength==0)&&(Dbranch!=nil)) {
- X bufscpy(&newdelnum, Dbranch);
- X newdnumlength = countnumflds(Dbranch);
- X }
- X if (newdnumlength==0) bufscpy(&newdelnum, "1.1");
- X else if (newdnumlength==1) bufscat(&newdelnum, ".1");
- X else if (newdnumlength>2) {
- X error("Branch point doesn't exist for %s.",newdelnum.string);
- X return false;
- X } /* newdnumlength == 2 is OK; */
- X olddeltanum=nil;
- X Head = &newdelta;
- X newdelta.next=nil;
- X return true;
- X }
- X if (newdnumlength==0) {
- X /* derive new revision number from locks */
- X switch (findlock(true, &targetdelta)) {
- X
- X default:
- X /* found two or more old locks */
- X return false;
- X
- X case 1:
- X /* found an old lock */
- X olddeltanum=targetdelta->num;
- X /* check whether locked revision exists */
- X if (!genrevs(olddeltanum,(char*)nil,(char*)nil,(char*)nil,&gendeltas)) return false;
- X if (targetdelta==Head) {
- X /* make new head */
- X newdelta.next=Head;
- X Head= &newdelta;
- X incnum(olddeltanum, &newdelnum);
- X } else if (!targetdelta->next && countnumflds(olddeltanum)>2) {
- X /* new tip revision on side branch */
- X targetdelta->next= &newdelta;
- X newdelta.next = nil;
- X incnum(olddeltanum, &newdelnum);
- X } else {
- X /* middle revision; start a new branch */
- X bufscpy(&newdelnum, "");
- X if (!addbranch(targetdelta,&newdelnum)) return false;
- X }
- X return true; /* successful use of existing lock */
- X
- X case 0:
- X /* no existing lock; try Dbranch */
- X /* update newdelnum */
- X if (StrictLocks || !myself(RCSstat.st_uid)) {
- X error("no lock set by %s",getcaller());
- X return false;
- X }
- X if (Dbranch) {
- X bufscpy(&newdelnum, Dbranch);
- X } else {
- X incnum(Head->num, &newdelnum);
- X }
- X newdnumlength = countnumflds(newdelnum.string);
- X /* now fall into next statement */
- X }
- X }
- X if (newdnumlength<=2) {
- X /* add new head per given number */
- X olddeltanum=Head->num;
- X if(newdnumlength==1) {
- X /* make a two-field number out of it*/
- X if (cmpnumfld(newdelnum.string,olddeltanum,1)==0)
- X incnum(olddeltanum, &newdelnum);
- X else
- X bufscat(&newdelnum, ".1");
- X }
- X if (cmpnum(newdelnum.string,olddeltanum) <= 0) {
- X error("deltanumber %s too low; must be higher than %s",
- X newdelnum.string, Head->num);
- X return false;
- X }
- X if (!(targetdelta=removelock(Head))) return false;
- X if (!genrevs(olddeltanum,(char*)nil,(char*)nil,(char*)nil,&gendeltas)) return false;
- X newdelta.next=Head;
- X Head= &newdelta;
- X } else {
- X /* put new revision on side branch */
- X /*first, get branch point */
- X tp = newdelnum.string;
- X for (i = newdnumlength - (newdnumlength&1 ^ 1); (--i); )
- X while (*tp++ != '.')
- X ;
- X *--tp = 0; /* Kill final dot to get old delta temporarily. */
- X if (!(targetdelta=genrevs(newdelnum.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas)))
- X return false;
- X olddeltanum = targetdelta->num;
- X if (cmpnum(olddeltanum, newdelnum.string) != 0) {
- X error("can't find branchpoint %s", newdelnum.string);
- X return false;
- X }
- X *tp = '.'; /* Restore final dot. */
- X if (!addbranch(targetdelta,&newdelnum)) return false;
- X }
- X return true;
- X}
- X
- X
- X
- X static int
- Xaddbranch(branchpoint,num)
- X struct hshentry *branchpoint;
- X struct buf *num;
- X/* adds a new branch and branch delta at branchpoint.
- X * If num is the null string, appends the new branch, incrementing
- X * the highest branch number (initially 1), and setting the level number to 1.
- X * the new delta and branchhead are in globals newdelta and newbranch, resp.
- X * the new number is placed into num.
- X * returns false on error.
- X */
- X{
- X struct branchhead *bhead, **btrail;
- X struct buf branchnum;
- X int result;
- X unsigned field, numlength;
- X static struct branchhead newbranch; /* new branch to be inserted */
- X
- X numlength = countnumflds(num->string);
- X
- X if (branchpoint->branches==nil) {
- X /* start first branch */
- X branchpoint->branches = &newbranch;
- X if (numlength==0) {
- X bufscpy(num, branchpoint->num);
- X bufscat(num, ".1.1");
- X } else if (numlength&1)
- X bufscat(num, ".1");
- X newbranch.nextbranch=nil;
- X
- X } else if (numlength==0) {
- X /* append new branch to the end */
- X bhead=branchpoint->branches;
- X while (bhead->nextbranch) bhead=bhead->nextbranch;
- X bhead->nextbranch = &newbranch;
- X bufautobegin(&branchnum);
- X getbranchno(bhead->hsh->num, &branchnum);
- X incnum(branchnum.string, num);
- X bufautoend(&branchnum);
- X bufscat(num, ".1");
- X newbranch.nextbranch=nil;
- X } else {
- X /* place the branch properly */
- X field = numlength - (numlength&1 ^ 1);
- X /* field of branch number */
- X btrail = &branchpoint->branches;
- X while (0 < (result=cmpnumfld(num->string,(*btrail)->hsh->num,field))) {
- X btrail = &(*btrail)->nextbranch;
- X if (!*btrail) {
- X result = -1;
- X break;
- X }
- X }
- X if (result < 0) {
- X /* insert/append new branchhead */
- X newbranch.nextbranch = *btrail;
- X *btrail = &newbranch;
- X if (numlength&1) bufscat(num, ".1");
- X } else {
- X /* branch exists; append to end */
- X bufautobegin(&branchnum);
- X getbranchno(num->string, &branchnum);
- X targetdelta=genrevs(branchnum.string,(char*)nil,
- X (char*)nil,(char*)nil,&gendeltas);
- X bufautoend(&branchnum);
- X if (!targetdelta) return false;
- X olddeltanum=targetdelta->num;
- X if (cmpnum(num->string,olddeltanum) <= 0) {
- X error("deltanumber %s too low; must be higher than %s",
- X num->string,olddeltanum);
- X return false;
- X }
- X if (!removelock(targetdelta)) return false;
- X if (numlength&1) incnum(olddeltanum,num);
- X targetdelta->next= &newdelta;
- X newdelta.next=nil;
- X return true; /* Don't do anything to newbranch */
- X }
- X }
- X newbranch.hsh = &newdelta;
- X newdelta.next=nil;
- X return true;
- X}
- X
- X
- X
- X static void
- Xincnum(onum,nnum)
- X const char *onum;
- X struct buf *nnum;
- X/* Increment the last field of revision number onum by one and
- X * place the result into nnum.
- X */
- X{
- X register const char *sp;
- X register char *tp;
- X register unsigned i;
- X
- X sp = onum;
- X bufalloc(nnum, strlen(sp)+2);
- X tp = nnum->string;
- X for (i=countnumflds(sp); (--i); ) {
- X while (*sp != '.') *tp++ = *sp++;
- X *tp++ = *sp++; /* copy dot also */
- X }
- X VOID sprintf(tp, "%d", atoi(sp)+1);
- X}
- X
- X
- X
- X static struct hshentry *
- Xremovelock(delta)
- Xstruct hshentry * delta;
- X/* function: Finds the lock held by caller on delta,
- X * removes it, and returns a pointer to the delta.
- X * Prints an error message and returns nil if there is no such lock.
- X * An exception is if !StrictLocks, and caller is the owner of
- X * the RCS file. If caller does not have a lock in this case,
- X * delta is returned.
- X */
- X{
- X register struct lock * next, * trail;
- X const char *num;
- X struct lock dummy;
- X int whomatch, nummatch;
- X
- X num=delta->num;
- X dummy.nextlock=next=Locks;
- X trail = &dummy;
- X while (next!=nil) {
- X whomatch = strcmp(getcaller(), next->login);
- X nummatch=strcmp(num,next->delta->num);
- X if ((whomatch==0) && (nummatch==0)) break;
- X /*found a lock on delta by caller*/
- X if ((whomatch!=0)&&(nummatch==0)) {
- X error("revision %s locked by %s",num,next->login);
- X return nil;
- X }
- X trail=next;
- X next=next->nextlock;
- X }
- X if (next!=nil) {
- X /*found one; delete it */
- X trail->nextlock=next->nextlock;
- X Locks=dummy.nextlock;
- X next->delta->lockedby=nil; /* reset locked-by */
- X return next->delta;
- X } else {
- X if (StrictLocks || !myself(RCSstat.st_uid)) {
- X error("no lock set by %s for revision %s",getcaller(),num);
- X return nil;
- X } else {
- X return delta;
- X }
- X }
- X}
- X
- X
- X
- X static const char *
- Xgetdate()
- X/* Return a pointer to the current date. */
- X{
- X static char buffer[datesize]; /* date buffer */
- X time_t t;
- X
- X if (!buffer[0]) {
- X t = time((time_t *)0);
- X if (t == -1)
- X faterror("time not available");
- X time2date(t, buffer);
- X }
- X return buffer;
- X}
- X
- X
- X static const char *
- Xxpandfile (unexfname,dir,delta)
- X const char *unexfname, *dir;
- X const struct hshentry *delta;
- X/* Function: Reads file unexpfname and copies it to a
- X * file in dir, performing keyword substitution with data from delta.
- X * returns the name of the expanded file if successful, nil otherwise.
- X */
- X{
- X const char *targetfname;
- X FILE * unexfile, *exfile;
- X
- X targetfname = makedirtemp(dir,0);
- X errno = 0;
- X if (!(unexfile = fopen(unexfname, "r"))) {
- X eerror(unexfname);
- X return nil;
- X }
- X errno = 0;
- X if (!(exfile = fopen(targetfname, "w"))) {
- X eerror(targetfname);
- X error("can't expand file %s",unexfname);
- X ffclose(unexfile);
- X return nil;
- X }
- X if (Expand == OLD_EXPAND)
- X fastcopy(unexfile,exfile);
- X else
- X while (0 < expandline(unexfile,exfile,delta,false,(FILE*)nil))
- X ;
- X ffclose(unexfile);ffclose(exfile);
- X return targetfname;
- X}
- X
- X
- X static int
- Xmustcheckin (unexfname,delta)
- X const char *unexfname;
- X const struct hshentry *delta;
- X/* Function: determines whether checkin should proceed.
- X * Compares the workfilename with unexfname, disregarding keywords.
- X * If the 2 files differ, returns true. If they do not differ, asks the user
- X * whether to return true or false (i.e., whether to checkin the file anyway);
- X * the default answer is false.
- X * Shortcut: If forceciflag is set, mustcheckin() always returns true.
- X */
- X{
- X int result;
- X
- X if (forceciflag) return true;
- X
- X if (!rcsfcmp(workfilename,unexfname,delta)) return true;
- X /* If files are different, must check them in. */
- X
- X /* files are the same */
- X if (!(result = yesorno(false,
- X "File %s is unchanged with respect to revision %s\ncheckin anyway? [ny](n): ",
- X workfilename, delta->num
- X ))) {
- X error("%scheckin aborted",
- X !quietflag && ttystdin() ? "" : "file is unchanged; "
- X );
- X }
- X return result;
- X}
- X
- X
- X
- X
- X/* --------------------- G E T L O G M S G --------------------------------*/
- X
- X
- X static struct cbuf
- Xgetlogmsg()
- X/* Function: obtains a log message and returns a pointer to it.
- X * If a log message is given via the -m option, a pointer to that
- X * string is returned.
- X * If this is the initial revision, a standard log message is returned.
- X * Otherwise, reads a character string from the terminal.
- X * Stops after reading EOF or a single '.' on a
- X * line. getlogmsg prompts the first time it is called for the
- X * log message; during all later calls it asks whether the previous
- X * log message can be reused.
- X * returns a pointer to the character string; the pointer is always non-nil.
- X */
- X{
- X static const char
- X emptych[] = "*** empty log message ***",
- X initialch[] = "Initial revision";
- X static const struct cbuf
- X emptylog = { emptych, sizeof(emptych)-sizeof(char) },
- X initiallog = { initialch, sizeof(initialch)-sizeof(char) };
- X static struct buf logbuf;
- X static struct cbuf logmsg;
- X
- X int cin;
- X register char *tp;
- X register size_t i;
- X register const char *p;
- X const char *caller, *date;
- X
- X if (keepflag) {
- X /* generate std. log message */
- X caller = getcaller();
- X p = date = getdate();
- X while (*p++ != '.')
- X ;
- X i = strlen(caller);
- X bufalloc(&logbuf, sizeof(ciklog)+strlen(caller)+4+datesize);
- X tp = logbuf.string;
- X VOID sprintf(tp,
- X "%s%s at %s%.*s/%.2s/%.2s %.2s:%.2s:%s",
- X ciklog, caller,
- X date[2]=='.' && VERSION(5)<=RCSversion ? "19" : "",
- X p-date-1, date,
- X p, p+3, p+6, p+9, p+12
- X );
- X logmsg.string = tp;
- X logmsg.size = strlen(tp);
- X return logmsg;
- X }
- X
- X if (msg.size) return msg;
- X
- X if (!olddeltanum && (
- X cmpnum(newdelnum.string,"1.1")==0 ||
- X cmpnum(newdelnum.string,"1.0")==0
- X ))
- X return initiallog;
- X
- X if (logmsg.size) {
- X /*previous log available*/
- X if (yesorno(true, "reuse log message of previous file? [yn](y): "))
- X return logmsg;
- X }
- X
- X /* now read string from stdin */
- X if (feof(stdin))
- X faterror("can't reread redirected stdin for log message; use -m");
- X if (ttystdin())
- X aputs("enter log message:\n(terminate with single '.' or end of file)\n>> ",stderr);
- X
- X i = 0;
- X tp = logbuf.string;
- X while ((cin = getcstdin()) != EOF) {
- X if (cin=='\n') {
- X if (i && tp[i-1]=='.' && (i==1 || tp[i-2]=='\n')) {
- X /* Remove trailing '.'. */
- X --i;
- X break;
- X }
- X if (ttystdin()) aputs(">> ", stderr);
- X }
- X bufrealloc(&logbuf, i+1);
- X tp = logbuf.string;
- X tp[i++] = cin;
- X /*SDELIM will be changed to double SDELIM by putdtext*/
- X } /* end for */
- X
- X /* now check whether the log message is not empty */
- X logmsg = cleanlogmsg(tp, i);
- X if (logmsg.size)
- X return logmsg;
- X return emptylog;
- X}
- X
- X/* Make a linked list of Symbolic names */
- X
- X static void
- Xaddassoclst(flag, sp)
- Xint flag;
- Xchar * sp;
- X{
- X struct Symrev *pt;
- X
- X pt = talloc(struct Symrev);
- X pt->ssymbol = sp;
- X pt->override = flag;
- X pt->nextsym = nil;
- X if (lastassoc)
- X lastassoc->nextsym = pt;
- X else
- X assoclst = pt;
- X lastassoc = pt;
- X return;
- X}
- END_OF_FILE
- if test 34353 -ne `wc -c <'src/ci.c'`; then
- echo shar: \"'src/ci.c'\" unpacked with wrong size!
- fi
- # end of 'src/ci.c'
- fi
- echo shar: End of archive 1 \(of 12\).
- cp /dev/null ark1isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 12 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
-